package org.yaac.server.egql.processor;

import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.newArrayListWithExpectedSize;

import java.util.List;

import org.yaac.server.egql.InsertItem;
import org.yaac.server.egql.InsertStatement;
import org.yaac.server.egql.evaluator.EvaluationResult;
import org.yaac.server.egql.processor.ProcessData.ProcessDataRecord;
import org.yaac.server.util.DatastoreUtil;
import org.yaac.shared.egql.ResultStatus;
import org.yaac.shared.file.FileDownloadPath;

import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;

/**
 * @author Max Zhu (thebbsky@gmail.com)
 *
 */
public class InsertProcessor implements Processor {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	private InsertStatement stmt;
	
	private int insertCount;
	
	public InsertProcessor(InsertStatement stmt) {
		this.stmt = stmt;
		this.insertCount = 0;
	}

	@Override
	public ProcessData process(ProcessContext context, ProcessData input) {
		// entities to insert
		List<Entity> entities = newArrayListWithExpectedSize(input.getRecords().size());
		
		for (ProcessDataRecord record : input.getRecords()) {
			Entity e = null;
			
			InsertItem keyItem = stmt.keyItem();
			if (keyItem == null) {
				e = new Entity(stmt.getKind());
			} else {
				// TODO : add validation to make sure it always returns a key
				e = new Entity((Key) keyItem.getE().evaluate(record).getPayload());  
			}
			
			for(InsertItem item : stmt.propertyItems()) {
				Object val = item.getE().evaluate(record).getPayload();	// evaluation type
				val = DatastoreUtil.toDatastoreType(val);	// datastore type
				if (item.getIndexed()) {
					e.setProperty(item.getIdentity(), val);
				} else {
					e.setUnindexedProperty(item.getIdentity(), val);
				}
			}
			
			entities.add(e);
		}
		
		DatastoreServiceFactory.getAsyncDatastoreService().put(entities);
		
		this.insertCount += entities.size();
		
		if (context.getStatus() == ResultStatus.FINISHED) {	// append result on finish
			return new ProcessData().addRecord(new ProcessDataRecord() {
				@Override
				public FileDownloadPath lookupFileReference(Integer index) {
					// should not be called
					throw new IllegalArgumentException();
				}
				
				@Override
				public EvaluationResult lookup(String name) {
					// should not be called
					throw new IllegalArgumentException();
				}
				
				@Override
				public Iterable<EvaluationResult> asIterable() {
					return newArrayList(
							new EvaluationResult("Total number of records inserted : " + insertCount).withTitle("Info"));
				}
			});
		} else {	// simply return dummy data
			return new ProcessData();
		}
	}

}
